package org.eraser.security;

import org.eraser.security.authentication.oidc.EndUserService;
import org.eraser.security.authentication.ManMachineVerificationFilter;
import org.eraser.security.authentication.Oauth2CodeAuthenticationFilter;
import org.eraser.security.authentication.handler.LoginFailureHandler;
import org.eraser.security.authentication.handler.LoginSuccessHandler;
import org.eraser.security.authentication.provider.EUDaoAuthenticationProvider;
import org.eraser.security.authentication.provider.SMSAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.*;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.ArrayList;
import java.util.List;

@AutoConfigureAfter({SecurityCoreConfig.class})
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    /**
     * 身份验证执行器 基于用户名密码验证
     */
    @Autowired
    private EUDaoAuthenticationProvider daoAuthenticationProvider;

    /**
     * 身份验证执行器 基于手机号和短信验证码验证
     */
    @Autowired
    private SMSAuthenticationProvider smsVerificationCodeProvider;

    /**
     * UserDetailsService 的实现 检索用户信息
     */
    @Autowired
    private EndUserService userDetailsService;

    /**
     * 图形验证码过滤器 "人机验证" 在 UsernamePasswordAuthenticationFilter 之前
     */
    @Autowired
    private ManMachineVerificationFilter verificationCodeFilter;

    /**
     * oauth2.0 认证流程的过滤器
     */
    @Autowired
    private Oauth2CodeAuthenticationFilter oauth2CodeFilter;

    /**
     * 登陆成功处理器
     */
    @Autowired
    private LoginSuccessHandler successHandler;

    /**
     * 登陆失败处理器
     */
    @Autowired
    private LoginFailureHandler failureHandler;


    private ProviderManager authenticationManager() {
        List<AuthenticationProvider> providers = new ArrayList<>();
        providers.add(daoAuthenticationProvider);
        providers.add(smsVerificationCodeProvider);
        // AuthenticationConfiguration 认证管理器配置 也可以获取认证管理器
        return new ProviderManager(providers);
    }


    /**
     * 过滤器链配置
     * <p>
     * SpringSecurity 6.1.2 鉴权:
     * AuthorizationFilter 鉴权过滤器 取代了 FilterSecurityInterceptor 拦截器。触发鉴权流程。
     * AuthorizationManager 授权管理器 取代了 AccessDecisionManager 访问决策管理器, AccessDecisionVoter 投票器接口。
     * AuthorizationDecision 授权管理器 执行完 check 方法会返回这个 "授权决定"
     * 常用的投票器:
     * WebExpressionVoter 被 WebExpressionAuthorizationManager 代替。
     * RoleHierarchyVoter 被 AuthorityAuthorizationManager 代替。
     * <p>
     * 方法级别的权限控制:
     * MethodSecurityExpressionHandler。
     * AuthorizationManagerAfterMethodInterceptor 和 AuthorizationManagerBeforeMethodInterceptor 取代了 MethodSecurityInterceptor AOP方法拦截器。
     * <p>
     * FilterOrderRegistration 这个类负责过滤器执行顺序
     * <p>
     * 访问控制的配置:
     * authorizeHttpRequests(authorize -> authorize.requestMatchers(InitializeConfig.WHITE_URL).permitAll().anyRequest().authenticated())
     * AuthorizationFilter 这个过滤器会根据以上配置执行相关逻辑
     * <p>
     * RequestMatcherDelegatingAuthorizationManager 详解:
     * (请求匹配器委托授权管理器-也是实现的 AuthorizationManager) 此类中的 mappings 变量就是我们配置的访问规则
     * 它里面是一个个的 RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>> 其基于 MvcRequestMatcher 进行匹配
     * AuthenticatedAuthorizationManager 这个类来判断当前请求是否是 "已认证的" 如果不是则抛出 AccessDeniedException("Access Denied") 异常
     * <p>
     * 如果不是登陆状态 ExceptionTranslationFilter 过滤器中会捕获 AccessDeniedException 异常并重定向到登录页
     * <p>
     * LoginUrlAuthenticationEntryPoint 这个类负责重定向 他是 AuthenticationEntryPoint 的实现类
     * <p>
     * AbstractAuthenticationProcessingFilter 类中
     * this.sessionStrategy.onAuthentication(authenticationResult, request, response);
     * 这段逻辑是在执行 1session创建 2session存储 3session过期 4并发session控制（一个账号同时只能有一个有效session） 5防止Session Fixation攻击的功能
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                // todo 安全头
                .headers(headersConfigurer -> {
                    headersConfigurer.httpStrictTransportSecurity(HeadersConfigurer.HstsConfig::disable);
                })
                .formLogin((formLogin) -> formLogin
                        .usernameParameter(SecurityCoreConfig.USERNAME)
                        .passwordParameter(SecurityCoreConfig.PASSWORD)
                        .loginProcessingUrl(SecurityCoreConfig.LOGIN_PROCESSES_URL)
                        .successHandler(successHandler)
                        .failureHandler(failureHandler)
                )
                .cors(CorsConfigurer::disable)

                // todo 浏览器应用的安全防护
                .csrf(CsrfConfigurer::disable)
                .httpBasic(HttpBasicConfigurer::disable)
                .authorizeHttpRequests(authorize -> authorize.requestMatchers(SecurityCoreConfig.WHITE_URL).permitAll().anyRequest().authenticated())
                .addFilterBefore(verificationCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterAfter(oauth2CodeFilter, AuthorizationFilter.class)
                .userDetailsService(userDetailsService)
                .authenticationManager(authenticationManager());

        return http.build();
    }


}
